# -*- coding: utf-8 -*-
"""
Created on Sat Jun  8 00:37:29 2019

@author: Administrator
"""
import sys
sys.path.append('./benchmark/')

import math
import numpy as np
from matplotlib import pyplot as plt
from benchmark import benchmark as bench

class SimulatedAnneal:
    
    def __init__(self, upper, lower, chain, coef_k, temp, generation):
        self.upper = upper
        self.lower = lower
        self.chain = chain
        self.coef_k = coef_k
        self.temp = temp
        self.generation = generation
    
    def deploy_func(self, func):
        self.func = func
    
    def run(self):
        # 初始化参数
        x_init = np.random.rand() * (self.upper - self.lower) + self.lower
        x_best = x_init
        x_now = x_init
        result = []
        fitness = []
        
        fitness.append(self.func(x_init))
        result.append(min(fitness))
        
        # 迭代
        for gen in range(2,self.generation+1):
            # 等温过程
            # 判断在当前温度下是否达到平衡
            for l in range(self.chain):
                # metropolis准则对新解进行取舍
                x_new = x_now + np.random.rand() * (self.upper - self.lower) + self.lower
                
                # 边界处理
                if x_new < self.lower:
                    x_new = self.lower
                if x_new > self.upper:
                    x_new = self.upper
                
                # 如果解适应度更高，直接接受解
                # 如果适应度更低，以概率p接受解
                if self.func(x_new) < self.func(x_now):
                    x_now = x_new
                else:
                    p = math.exp(-(self.func(x_new) - self.func(x_now)) / self.temp)
                    if p > np.random.rand():
                        x_now = x_new
                
                # 记录等温过程最优解
                if self.func(x_now) < self.func(x_best):
                    x_best = x_now
            
            # 等温过程结束
            # 温度衰减
            self.temp = self.coef_k * self.temp
            result.append(self.func(x_best))
            
            if self.temp < 0.01:
                break
            
        return result
    
    def set_plot(self, title, xlabel, ylabel, plot_pattern=None):
        plt.title(title)
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        self.plot_pattern = plot_pattern
    
    def plot(self, result):
        if self.plot_pattern == None:
            plt.plot(result)
        plt.plot(result, self.plot_pattern)
        plt.show()
    

upper = 20
lower = -20
chain = 200
coef_k = 0.998
temp = 100
generation = 500

sa = SimulatedAnneal(upper, lower, chain, coef_k, temp, generation)
sa.deploy_func(bench.schwefel2_22)
result = sa.run()
print(result)
sa.set_plot("simulated anneal", "generation", "fitness")
sa.plot(result)
